=begin rdoc
Unit tests for event class.

Author::    Matthias Woehrle  (mailto:matthias.woehrle@tik.ee.ethz.ch)
Copyright:: Copyright (c) 2008 ETH Zurich
License::   

TODO: Use this for time order as well! Do we have something to do for that to work?

MISSING:
=end

#TODO: maybe randomized testing is not so clever - I saw once a fail
#      for test_order_generate_of but cannot reproduce it
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")

require 'periodic'
require 'test/unit'

#Extend the class to be able to look inside
class Periodic
  attr_reader :last, :current_prefix, :period, :of_detected
end

class PeriodicMock < Periodic
  #Intialize a periodic variable with a range
  def initialize(nodeid, range, last, prefix, of)
    @last = {nodeid=>last}
    @current_prefix = {nodeid=>prefix}
    #todo - this works for ranges only at the moment
    min = range.first
    max = range.last
    @period = max - min + 1
    @of_detected = {nodeid => of}
    @offset = range.first
    puts "Mock using lower = #{range.first} and upper=#{range.last} and period=#{@period} and prefix=#{@current_prefix} and of_detected = #{of_detected}" if $DEBUG
    self
  end
end

LOWER = 3
UPPER = 400
PREFIX = 197

#TODO: Deterministic tests with edge conditions are better than these random tests!
class TestPeriod < Test::Unit::TestCase

  def setup
    @nodeid = 1
    #may be extended for arbitrary lower values
    @lower = LOWER
    @upper = UPPER
    @period_test = Periodic.new(LOWER..UPPER)
    @period = UPPER-LOWER+1
    @prefix = PREFIX
  end

  #test period initialization
  def test_init_period
    assert_equal(@period, @period_test.period)
  end
  
  #Note that last internally has already the offset subtracted

  #test the output if no overflow was detected before
  def test_order_no_of
     last = (rand * 0.1 * @period).to_i
     current = (rand * 0.9 * @period + @lower).to_i #3..361
     puts "test_order_no_of using last = #{last} and current=#{current}" if $DEBUG     
     periodic_mock1 = PeriodicMock.new(@nodeid,@lower..@upper, last, @prefix, 0)
     assert_equal(current - LOWER + @period * @prefix, periodic_mock1.order(current, @nodeid))
  end

  #test the output if an overflow was detected before
  def test_order_of
    last = (rand * 0.1 * @period).to_i 
    current = (rand * 0.1 * @period + @lower).to_i #3..42
     puts "test_order_of using last = #{last} and current=#{current}" if $DEBUG         
    periodic_mock2 = PeriodicMock.new(@nodeid,@lower..@upper, last, @prefix, 1)
    assert_equal(current - LOWER + @period * (@prefix + 1), periodic_mock2.order(current, @nodeid))
  end

  #test the output and the overflow state if an overflow is to be generated
  def test_order_generate_of
    last = (rand * 0.09 * @period + 359).to_i
    current = (rand * 0.1 * @period + @lower).to_i #3..42
     puts "test_order_generate_of using last = #{last} and current=#{current}" if $DEBUG         
    periodic_mock3 = PeriodicMock.new(@nodeid,@lower..@upper, last, @prefix, 0)
    assert_equal(current - LOWER + @period * (@prefix + 1), periodic_mock3.order(current, @nodeid))
    assert_equal(1, periodic_mock3.of_detected[@nodeid])   
    assert_equal(@prefix, periodic_mock3.current_prefix[@nodeid])       
  end

  #test the output and the overflow state if an overflow is to be reset
  def test_order_reset_of
    last = (rand * 0.1 * @period - 1).to_i
    current = (rand * 0.1 * @period + @lower - 1).to_i #2..41
     puts "test_order_reset_of using last = #{last} and current=#{current}" if $DEBUG         
    periodic_mock4 = PeriodicMock.new(@nodeid,@lower..@upper, last, @prefix, 1)
    assert_equal(current - LOWER + @period * (@prefix + 1), periodic_mock4.order(current, @nodeid))   
    assert_equal(0, periodic_mock4.of_detected[@nodeid])   
    assert_equal(@prefix+1, periodic_mock4.current_prefix[@nodeid])     
  end
  


end


#TODO: Deterministic tests with edge conditions are better than these random tests!
class TestPeriod_Multiple < Test::Unit::TestCase

  def setup
    @nodeid = [1,3]
    #may be extended for arbitrary lower values
    @lower = LOWER
    @upper = UPPER
    @period = UPPER-LOWER+1
    @prefix_array = [192,201]
  end
  def test_order_with_multiple_nodes
    last = [400,12]
    current = [3,13]
    periodic_mocks = {@nodeid[0]=>PeriodicMock.new(@nodeid[0], @lower..@upper, last[0], @prefix_array[0], 0), 
    @nodeid[1]=>PeriodicMock.new(@nodeid[1], @lower..@upper, last[1], @prefix_array[1], 0)}

    #here is an overflow detected but there is no update on the prefix yet
    assert_equal(current[0] - LOWER + @period * (@prefix_array[0] + 1),  periodic_mocks[@nodeid[0]].order(current[0], @nodeid[0]))   
    assert_equal(1, periodic_mocks[@nodeid[0]].of_detected[@nodeid[0]])   
    assert_equal(@prefix_array[0], periodic_mocks[@nodeid[0]].current_prefix[@nodeid[0]])     

    #here there should be no work performed since there was only an increment
    assert_equal(current[1] - LOWER + @period * @prefix_array[1],  periodic_mocks[@nodeid[1]].order(current[1], @nodeid[1]))   
    assert_equal(0, periodic_mocks[@nodeid[1]].of_detected[@nodeid[1]])   
    assert_equal(@prefix_array[1], periodic_mocks[@nodeid[1]].current_prefix[@nodeid[1]])         
  end
  
  def test_order_with_multiple_nodes_part2
    last = [3,4]
    current = [4,5]
    periodic_mocks = {@nodeid[0]=>PeriodicMock.new(@nodeid[0], @lower..@upper, last[0], @prefix_array[0], 1), 
    @nodeid[1]=>PeriodicMock.new(@nodeid[1], @lower..@upper, last[1], @prefix_array[1], 0)}

    #here is an overflow reset and an predix increment
    assert_equal(current[0] - LOWER + @period * (@prefix_array[0]+1),  periodic_mocks[@nodeid[0]].order(current[0], @nodeid[0]))   
    assert_equal(0, periodic_mocks[@nodeid[0]].of_detected[@nodeid[0]])   
    assert_equal(@prefix_array[0] + 1, periodic_mocks[@nodeid[0]].current_prefix[@nodeid[0]])     

    #here there should be no work performed since there was only an increment
    assert_equal(current[1] - LOWER + @period * @prefix_array[1],  periodic_mocks[@nodeid[1]].order(current[1], @nodeid[1]))   
    assert_equal(0, periodic_mocks[@nodeid[1]].of_detected[@nodeid[1]])   
    assert_equal(@prefix_array[1], periodic_mocks[@nodeid[1]].current_prefix[@nodeid[1]])         
  end
  
end
    